package service

import (
	"context"
	"errors"
	"fmt"
	"gin-luban-server/global"
	"gin-luban-server/model"
	"gin-luban-server/model/request"
	"gin-luban-server/utils"
	"github.com/360EntSecGroup-Skylar/excelize/v2"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"log"
	"strconv"
	"strings"
	"time"
)

var ctx = context.Background()

/*
集群操作
增、删、改、查
*/

func AddRedisCluster(redisCluster model.RedisCluster) (err error) {
	if !errors.Is(global.GVA_DB.Where("name = ? ", redisCluster.Name).First(&model.RedisCluster{}).Error, gorm.ErrRecordNotFound) {
		return errors.New("存在相同集群")
	}
	err = global.GVA_DB.Create(&redisCluster).Error
	if err != nil {
		return err
	}
	// 1. 获取创建node表  其他信息 通过 redis-api获取
	redisPassword := redisCluster.Password
	redisClusterAddress := redisCluster.Nodes
	redisNodeArray := strings.Split(redisClusterAddress, ",")
	redisCluster.ClusterState = "health"
	redisCluster.ClusterKnownNodes = len(redisNodeArray)
	for _, redisNodeAddr := range redisNodeArray {
		// 获取节点信息
		fmt.Println(redisNodeAddr)
		var redisNodeInfo = new(model.RedisNodeInfo)
		var redisNodeOtherInfo = new(model.RedisNodeOtherInfo)
		var redisNode model.RedisNode
		redisNode.RedisNodeInfo = redisNodeInfo
		redisNode.Node = redisNodeAddr
		//  1. 获取redis原始数据
		//err, info := utils.GetRedisInfo(redisNodeAddr, redisPassword)
		rdb, _ := utils.GetRedisClient(redisNodeAddr, redisPassword, redisCluster.XShellProxy, 0)
		info, err := rdb.Info(ctx).Result()

		if err != nil {
			global.GVA_LOG.Error("连接redis失败", zap.Any("err", err.Error()))

			redisNode.LinkState = "bad"
			redisCluster.ClusterState = "bad"
		} else {
			redisNode.LinkState = "health"
		}

		if redisNode.LinkState == "health" {
			// 2. 格式化数据成节点信息需要的
			err = utils.FormatRedisInfo(info, redisNodeInfo, redisNodeOtherInfo)
			fmt.Printf("redis节点测试: %s\n", redisNodeInfo.NodeRole)
			redisNode.RedisNodeInfo = redisNodeInfo
		}
		redisNode.ClusterID = redisCluster.ID
		// 3. 数据写入 node节点DB中
		err = global.GVA_DB.Create(&redisNode).Error
		if err != nil {
			return err
			global.GVA_LOG.Error("host写入数据库失败", zap.Any("err", err.Error()))
		}

		// 4. 综合节点数据 - 节点中master的信息 在写入cluster中 (集群中的master)
		log.Println("redisNode.NodeRole:", redisNode.NodeRole)
		if redisNode.NodeRole == "master" {
			redisCluster.ClusterMaster = redisNodeAddr
			redisCluster.RedisNodeInfo = redisNodeInfo
			//global.DB.Debug().Where("name = ? ", redisCluster.Name).First(&model.RedisCluster{}).Updates(model.RedisCluster{RedisNodeInfo: redisNodeInfo})
			if err != nil {
				global.GVA_LOG.Error("更新集群中的 信息失败", zap.Any("err", err.Error()))
			}
		} else {
			global.GVA_LOG.Info("slave角色的节点", zap.Any("node", redisNode.Node))

		}
		global.GVA_DB.Save(redisCluster)
	}
	return err
}

// 删
func DeleteRedisCluster(id float64) (err error) {
	// 1. 判断不存在没有在其他 表中使用 虚拟机中 <- 待补充
	var redisCluster model.RedisCluster
	var redisNode  model.RedisNode
	// 2. 删除
	err = global.GVA_DB.Where("id = ?", id).Delete(&redisCluster).Error

	// 2.1 删除关联节点表中数据
	if err == nil {
		err = global.GVA_DB.Where("cluster_id = ?", id).Delete(&redisNode).Error
	}

	return err
}

// 改
func UpdateRedisCluster(redisCluster model.RedisCluster) (err error) {
	var total int64
	var redisList []model.RedisCluster
	err = global.GVA_DB.Where("name = ? AND id != ?",redisCluster.Name,redisCluster.ID).Find(&redisList).Count(&total).Error
	if total >= 1 {
		return errors.New("存在相同集群名称!")
	}
	// 通过唯一ID进行更新
	err = global.GVA_DB.Where("id = ?", redisCluster.ID).First(&model.RedisCluster{}).Updates(&redisCluster).Error
	return err
}

// 查GetRedisClusterById
// 查
func GetRedisClusterById(id float64) (err error, redisCluster model.RedisCluster) {
	err = global.GVA_DB.Preload("Project").Where("id = ?", id).First(&redisCluster).Error
	return err, redisCluster
}


// 查
func GetRedisClusterList(redisCluster model.RedisCluster, info request.PageInfo, desc bool) (err error, list interface{}, total int64) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.GVA_DB.Model(&model.RedisCluster{})
	var redisClusterList []model.RedisCluster
	if redisCluster.Mode != "" {
		db = db.Where("mode = ?", redisCluster.Mode)
	}
	if redisCluster.Name != "" {
		db = db.Where("name like ?", "%" + redisCluster.Name + "%")
	}
	if redisCluster.ProjectCode != "" {
		db = db.Where("project_code = ?", redisCluster.ProjectCode)
	}
	err = db.Count(&total).Error

	if err != nil {
		return err, redisClusterList, total
	} else {
		db = db.Limit(limit).Offset(offset)
		err = db.Preload("Project").Find(&redisClusterList).Error
	}
	return err, redisClusterList, total

}

// 导出excel
func ParseRedisInfoList2Excel(Redis model.RedisCluster, filePath string) error {

	db := global.GVA_DB.Model(&model.RedisCluster{})
	var RedisList []model.RedisCluster

	if Redis.Name != "" {
		db = db.Where("name LIKE ?", "%"+Redis.Name+"%")
	}

	if Redis.Env != "" {
		db = db.Where("env = ?", Redis.Env)
	}
	// 是整型 1.单机 2.主从 3.哨兵
	if Redis.Mode != "" {
		db = db.Where("mode = ?", Redis.Mode)
	}

	if Redis.ProjectCode != "" {
		db = db.Where("project_code = ?", Redis.ProjectCode)
	}

	err := db.Preload("Project").Find(&RedisList).Error
	if err != nil {
		return err
	}
	// 写入 excel文件
	excel := excelize.NewFile()
	excel.SetSheetRow("Sheet1","A1",&[]string{"ID", "集群名", "环境", "节点列表", "库容量",  "所属项目", "状态"})

	for i, menu := range RedisList {
		axis := fmt.Sprintf("A%d",i+2)
		excel.SetSheetRow("Sheet1",axis,&[]interface{}{
			menu.ID,
			menu.Name,
			menu.Env,
			menu.Nodes,
			menu.MaxDB,
			menu.Project.ProjectName,
			utils.BoolToString(*menu.Status),      // 布尔值转成 字符串 正常|关闭
		})
	}
	global.GVA_LOG.Info("保存到EXCEL....")

	excel.SaveAs(filePath)
	return nil
}


/*
	操作redis的功能集合
*/
// 命令
func SendCommand(command model.GETRedisCommand) (err error, result interface{}) {

	// 1. 获取命令 获取redis中的集群 master和密码
	var redisCluster model.RedisCluster
	err = global.GVA_DB.Where("id = ?", command.ClusterID).First(&redisCluster).Error
	if err != nil {
		log.Println("数据库链接失败", err.Error())
	}
	// 2. 连接redis
	rdb, _ := utils.GetRedisClient(redisCluster.ClusterMaster, redisCluster.Password, redisCluster.XShellProxy, command.Database)
	_, err = rdb.Ping(ctx).Result()

	if err != nil {
		return err, result
	}

	if command.Command != "" {
		commandArray := strings.Fields(command.Command)
		//  []string 转成 []interface{}
		var commandInterface []interface{}
		for _, value := range commandArray {
			commandInterface = append(commandInterface, value)
		}

		// 3. 得到数据
		if strings.Contains(command.Command, "keys") {
			resultArray, _ := rdb.Keys(ctx, commandArray[1]).Result()
			result = strings.Join(resultArray, "\n")
		} else {
			// Db 不支持 keys功能
			result, err = rdb.Do(ctx, commandInterface...).Result()
		}

		// 命令错误 或者key不存在错误  都不做为错误 而应该做为返回结果
		if err != nil && (err.Error() == "redis: nil" || strings.Contains(err.Error(), "ERR")){
			result = err.Error()
			err = nil
		}
		//log.Println(result)
	}

	return err, result
}

//key
func KeyQuery(queryInfo model.KeyScanParams) (keyInfo map[string]interface{},err error) {
	// 1. 获取命令 获取redis中的集群 master和密码
	var redisCluster model.RedisCluster
	err = global.GVA_DB.Where("id = ?", queryInfo.ClusterID).First(&redisCluster).Error
	if err != nil {
		global.GVA_LOG.Error("数据库链接失败", zap.Any("err", err.Error()))

	}

	// 2. 连接redis
	rdb, _ := utils.GetRedisClient(redisCluster.ClusterMaster, redisCluster.Password, redisCluster.XShellProxy, queryInfo.Database)

	keyInfo = make(map[string]interface{})
	result, err := rdb.Get(ctx, queryInfo.Key).Result()
	keyInfo["value"] = result
	s, err := rdb.Type(ctx, queryInfo.Key).Result()
	keyInfo["type"] = s
	duration, err := rdb.TTL(ctx, queryInfo.Key).Result()
	keyInfo["ttl"] = duration / 1000/1000/1000
	//println(keyInfo , "----------")
	return  keyInfo, err
}

// redis scan key
func KeyScan(queryInfo model.KeyScanParams) (list []string, err error ) {
	// 1. 获取命令 获取redis中的集群 master和密码
	var redisCluster model.RedisCluster
	err = global.GVA_DB.Where("id = ?", queryInfo.ClusterID).First(&redisCluster).Error
	if err != nil {
		log.Println("数据库链接失败", err.Error())
	}

	// 2. 连接redis
	rdb, err := utils.GetRedisClient(redisCluster.ClusterMaster, redisCluster.Password, redisCluster.XShellProxy, queryInfo.Database)
	var cursor uint64
	list, cursor, err = rdb.Scan(ctx, cursor, queryInfo.Key, queryInfo.Count).Result()

	return list, err
}

//
func GetDBList(cluster_id string) (list []map[string]interface{}, err error) {
	// 1. 获取命令 获取redis中的集群 master和密码
	var redisCluster model.RedisCluster
	err = global.GVA_DB.Where("id = ?", cluster_id).First(&redisCluster).Error
	if err != nil {
		global.GVA_LOG.Error("数据库链接失败", zap.Any("err", err.Error()))

	}

	// 2. 连接redis
	rdb, _ := utils.GetRedisClient(redisCluster.ClusterMaster, redisCluster.Password, redisCluster.XShellProxy, 0)

	list, err = utils.GetDBList(rdb)
	return list, err
}

/*
	监控数据
	1. 定时监控

*/
func RedisMonitor() {
	var ch chan int
	// redis每次监控的间隔时间
	redisMonitorInterval := global.GVA_CONFIG.Redis.RedisMonitorInterval
	ticker := time.NewTicker(time.Second * time.Duration(redisMonitorInterval))
	go func() {
		for range ticker.C {
			go func() {
				fmt.Println("定时监控:", time.Now().Format("2006-01-02 15:04:05"))
			}()

			// 监控任务
			go getRedisInfo()

		}
		ch <- 1
	}()
	<-ch
}

// 定时采集数据的 函数
func getRedisInfo() {
	var redisClusterList []model.RedisCluster
	err := global.GVA_DB.Model(&model.RedisCluster{}).Find(&redisClusterList).Error
	if err != nil {
		global.GVA_LOG.Error("查询数据库失败", zap.Any("err", err.Error()))

	}
	// 1. 遍历集群
	for _, redisNodes := range redisClusterList {
		var redisClusterInfo model.RedisCluster
		redisClusterInfo.RedisNodeInfo = new(model.RedisNodeInfo)
		// 定义集群状态
		redisClusterInfo.ClusterState = "health"
		redisClusterInfo.ID = redisNodes.ID
		// 1. 遍历集群中的节点
		for _, node := range strings.Split(redisNodes.Nodes, ",") {
			log.Printf("监控节点: %s", node)
			// 1. 定义数据模型
			var redisNodeInfo = new(model.RedisNodeInfo)
			var redisNodeOtherInfo = new(model.RedisNodeOtherInfo)
			var redisMonitorInfo  model.RedisMonitorInfo
			var redisNode = new(model.RedisNode)
			// base信息赋值
			redisMonitorInfo.ClusterID = redisNodes.ID
			redisMonitorInfo.LinkState = "health"
			redisMonitorInfo.Node = node
			//  2. 获取redis原始数据
			//err, info := utils.GetRedisInfo(node, redisNodes.Password)
			rdb, err := utils.GetRedisClient(node, redisNodes.Password, redisNodes.XShellProxy, 0)
			var info string
			if err != nil {
				global.GVA_LOG.Error("连接redis失败", zap.Any("err", err.Error()))
				redisMonitorInfo.LinkState = "bad"
				redisClusterInfo.ClusterState = "bad"
				info = ""
			} else {
				info, err = rdb.Info(ctx).Result()
			}

			if redisMonitorInfo.LinkState == "health" {
				// 3. 格式化数据成节点信息需要的
				err = utils.FormatRedisInfo(info, redisNodeInfo, redisNodeOtherInfo)
				// 赋值采集的监控信息
				redisMonitorInfo.RedisNodeInfo = redisNodeInfo
				redisMonitorInfo.RedisNodeOtherInfo = redisNodeOtherInfo
				redisClusterInfo.RedisNodeInfo = redisNodeInfo
				// 获取集群的master节点
				if redisNodeInfo.NodeRole == "master" {
					redisClusterInfo.ClusterMaster = node
				}
			}

			// 4. 插入监控的的数据库中
			err = global.GVA_DB.Create(&redisMonitorInfo).Error
			if err != nil {
				//log.Println("插入数据库失败", err.Error())
				global.GVA_LOG.Error("插入数据库失败", zap.Any("err", err.Error()))
			}

			// 5. 更新redis节点库 - (当前信息)
			db := global.GVA_DB.Where("node = ? ", node).Find(&redisNode, "cluster_id", redisMonitorInfo.ClusterID).First(&redisNode)
			redisNode.RedisNodeInfo = redisNodeInfo
			redisNode.LinkState =  redisMonitorInfo.LinkState
			err = db.Save(&redisNode).Error
			if err != nil {
				//log.Println("更新数据库", err.Error())
				global.GVA_LOG.Error("更新数据库", zap.Any("err", err.Error()))
			}

		}

		// 2. 更新集群信息到数据库中
		err = global.GVA_DB.Where("id = ? ", redisClusterInfo.ID).First(&model.RedisCluster{}).Updates(model.RedisCluster{RedisNodeInfo: redisClusterInfo.RedisNodeInfo, ClusterMaster: redisClusterInfo.ClusterMaster,ClusterState: redisClusterInfo.ClusterState}).Error
		if err != nil {
			//log.Println("更新数据库", err.Error())
			global.GVA_LOG.Error("更新数据库", zap.Any("err", err.Error()))
		}



	}

}

// 接口函数
func GetInfoItemMonitorData(queryInfo model.RedisMonitorQueryParams) (err error, resultList []interface{}) {

	for _, node := range queryInfo.NodeList {
		log.Printf("查询节点: %s 的信息", node)
		db := global.GVA_DB.Model(&model.RedisMonitorInfo{})

		startTime, _ := strconv.ParseInt(queryInfo.StartTime, 10 ,64)
		endTime, _ := strconv.ParseInt(queryInfo.EndTime, 10 ,64)
		db = db.Debug().Where("updated_at > ? and updated_at < ? and cluster_id = ?",
			time.Unix(startTime,0).Format("2006-01-02 15:04:05"),
			time.Unix(endTime,0).Format("2006-01-02 15:04:05"), queryInfo.ClusterID)
		var nodeMonitorData []model.RedisMonitorInfo
		err := db.Where("node = ? ", node).Find(&nodeMonitorData).Error
		if err != nil {
			log.Println("数据库查询失败,", err.Error())
		}
		resultList = append(resultList, nodeMonitorData)
	}

	return  err, resultList

}



/*
Redis 库号相关
*/

// 增
func CreateRedisDatabase(Redis model.RedisDatabaseRecord) (err error) {
	// 查看官网和测试，验证此处不用where where支持AND的是string类型
	err = global.GVA_DB.Find(&model.RedisDatabaseRecord{},  "redis_number = ? AND cluster_id = ? OR  project_code = ? AND cluster_id = ? ",  Redis.RedisNumber, Redis.ClusterID,Redis.ProjectCode,Redis.ClusterID).First(&model.RedisDatabaseRecord{}).Error
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.New("redis库已经存在或项目已使用")
	}
	return global.GVA_DB.Create(&Redis).Error
}

// 删
func DeleteRedisDatabase(id float64) (err error) {
	var Redis model.RedisDatabaseRecord
	// 2. 删除
	err = global.GVA_DB.Where("id = ?", id).Delete(&Redis).Error
	return err
}

// 改
func UpdateRedisDatabase(Redis model.RedisDatabaseRecord) (err error) {
	var total int64
	var redisDatabase []model.RedisDatabaseRecord
	err = global.GVA_DB.Where("project_code = ? OR redis_number = ? AND id != ?",Redis.ProjectCode,Redis.RedisNumber,Redis.ID).Find(&redisDatabase).Count(&total).Error
	if total >= 1 {
		return errors.New("项目或库号已存在!")
	}
	// 通过唯一ID进行更新
	err = global.GVA_DB.Where("id = ?", Redis.ID).First(&model.RedisDatabaseRecord{}).Updates(&Redis).Error
	return err
}

// 查 获取Redis 库列表
func GetRedisDatabaseInfoList(info request.SearchCMDBRedisDatabaseParams) (err error, list interface{}, total int64) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	db := global.GVA_DB.Model(&model.RedisDatabaseRecord{})
	var RedisDatabaseList []model.RedisDatabaseRecord

	if info.RedisNumber != "" {
		db = db.Where("redis_number = ?", info.RedisNumber)
	}
	if info.ProjectCode != "" {
		db = db.Where("project_code = ?", info.ProjectCode)
	}
	// 获取指定集群中的库号
	if info.ClusterID != 0{
		db = db.Where("cluster_id = ?", info.ClusterID)
	}
	err = db.Count(&total).Error

	if err != nil {
		return err, RedisDatabaseList, total
	} else {
		db = db.Limit(limit).Offset(offset)
		err = db.Preload("Project").Preload("RedisCluster").Find(&RedisDatabaseList).Error
	}
	return err, RedisDatabaseList, total
}

